/********************************************************************************
 * Copyright (c) 2017, 2018 Bosch Connected Devices and Solutions GmbH.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * Contributors:
 *    Bosch Connected Devices and Solutions GmbH - initial contribution
 *
 * SPDX-License-Identifier: EPL-2.0
 ********************************************************************************/

/*
 * generated by Xtext 2.10.0
 */
package org.eclipse.mita.platform.scoping

import com.google.common.base.Predicate
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EReference
import org.eclipse.mita.base.expressions.ElementReferenceExpression
import org.eclipse.mita.base.scoping.TypeKindNormalizer
import org.eclipse.mita.base.types.ComplexType
import org.eclipse.mita.base.types.EnumerationType
import org.eclipse.mita.base.types.Expression
import org.eclipse.mita.base.types.TypesPackage
import org.eclipse.mita.platform.PlatformPackage
import org.eclipse.xtext.naming.QualifiedName
import org.eclipse.xtext.nodemodel.util.NodeModelUtils
import org.eclipse.xtext.resource.IEObjectDescription
import org.eclipse.xtext.scoping.IScope
import org.eclipse.xtext.scoping.Scopes
import org.eclipse.xtext.scoping.impl.FilteringScope
import org.eclipse.xtext.scoping.impl.ImportNormalizer
import org.eclipse.xtext.scoping.impl.ImportScope

/**
 * This class contains custom scoping description.
 * 
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping
 * on how and when to use it.
 */
class PlatformDSLScopeProvider extends AbstractPlatformDSLScopeProvider {
	
	val Predicate<IEObjectDescription> globalElementFilter = [ x |
		val inclusion = 
			(PlatformPackage.Literals.ABSTRACT_SYSTEM_RESOURCE.isSuperTypeOf(x.EClass)) ||
			(PlatformPackage.Literals.MODALITY.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.PARAMETER.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.OPERATION.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.ENUMERATION_TYPE.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.TYPE_KIND.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.VIRTUAL_FUNCTION.isSuperTypeOf(x.EClass));

		val exclusion = 
			(PlatformPackage.Literals.SIGNAL.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.NAMED_PRODUCT_TYPE.isSuperTypeOf(x.EClass))  ||
			(TypesPackage.Literals.ANONYMOUS_PRODUCT_TYPE.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.SUM_TYPE.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.SINGLETON.isSuperTypeOf(x.EClass)) ||
			(TypesPackage.Literals.STRUCTURE_TYPE.isSuperTypeOf(x.EClass)) ||
			(PlatformPackage.Literals.SIGNAL_PARAMETER.isSuperTypeOf(x.EClass)) 

		inclusion && !exclusion;
	]
	
	def scope_ElementReferenceExpression_reference(EObject context, EReference ref) {
		val delegateScope = delegate.getScope(context, ref);
		val superScope = new FilteringScope(delegateScope, globalElementFilter);
		val scope = (if(context instanceof ElementReferenceExpression) {
			if(context.isOperationCall && context.arguments.size > 0) {
				val owner = context.arguments.head.value;

				val ownerText = NodeModelUtils.findNodesForFeature(owner, ref)?.head?.text ?: "";
				val normalizer = new ImportNormalizer(QualifiedName.create(ownerText), true, false);
				new ImportScope(#[normalizer], superScope, null, null, false);
				
			}
		}) ?: superScope;
		val typeKindNormalizer = new TypeKindNormalizer();
		return new ImportScope(#[typeKindNormalizer], scope, null, null, false);
		
	}

//	def IScope scope_FeatureCall_feature(FeatureCall context, EReference reference) {
//		val owner = context.arguments.head.value;
//		var EObject element = owner.element;
//		
//		if (element === null) {
//			return getDelegate().getScope(context, reference);
//		}
//
//		val scope = IScope.NULLSCOPE;
//		val result = typeInferrer.infer(owner);
//		val ownerType = result?.type;
//		
//		return addScopeForType(ownerType, scope);
//	}

	def dispatch IScope addScopeForType(EnumerationType type, IScope scope) {
		return Scopes.scopeFor(type.getEnumerator(), scope);
	}

	def dispatch IScope addScopeForType(ComplexType type, IScope scope) {
		return Scopes.scopeFor(type.getAllFeatures(), scope);
	}
	
	def dispatch IScope addScopeForType(Void type, IScope scope) {
		return scope;
	}
	
	def dispatch getElement(Expression it) {
		null
	}
	
	def dispatch getElement(ElementReferenceExpression it) {
		reference
	}
}
